/*
 * FreeModbus Libary: STM8S Port
 * Copyright (C) 2020 lxk <lxiangkun@163.com>
 *   - Initial version and STM8S support
 * Modfications Copyright (C) 2006 Tran Minh Hoang:
 *   - STM8S support
 *   - RS485 support for DS75176
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

#include "stm8s.h"
#include <string.h>
#include "board_conf.h"
#include "port.h"
#include "utils.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/

/* ----------------------- Start implementation -----------------------------*/
uint8_t UART1_SetBaudrate(uint16_t baudrate_opt)
{
    uint32_t BaudRate;
    uint32_t BaudRate_Mantissa = 0, BaudRate_Mantissa100 = 0;
    
    BaudRate = opt_to_baudrate(baudrate_opt);
    
    /* Clear the LSB mantissa of UART1DIV  */
    UART1->BRR1 &= (uint8_t)(~UART1_BRR1_DIVM);  
    /* Clear the MSB mantissa of UART1DIV  */
    UART1->BRR2 &= (uint8_t)(~UART1_BRR2_DIVM);  
    /* Clear the Fraction bits of UART1DIV */
    UART1->BRR2 &= (uint8_t)(~UART1_BRR2_DIVF);  
    
    /* Set the UART1 BaudRates in BRR1 and BRR2 registers according to UART1_BaudRate value */
    BaudRate_Mantissa    = ((uint32_t)CLK_GetClockFreq() / (BaudRate << 4));
    BaudRate_Mantissa100 = (((uint32_t)CLK_GetClockFreq() * 100) / (BaudRate << 4));
    /* Set the fraction of UART1DIV  */
    UART1->BRR2 |= (uint8_t)((uint8_t)(((BaudRate_Mantissa100 - (BaudRate_Mantissa * 100)) << 4) / 100) & (uint8_t)0x0F); 
    /* Set the MSB mantissa of UART1DIV  */
    UART1->BRR2 |= (uint8_t)((BaudRate_Mantissa >> 4) & (uint8_t)0xF0); 
    /* Set the LSB mantissa of UART1DIV  */
    UART1->BRR1 |= (uint8_t)BaudRate_Mantissa;
    
    return TRUE;
}

BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
{
    /* prevent compiler warning. */
    (void)ucPORT;
    
    UART1_Init(ulBaudRate, 
               UART1_WORDLENGTH_8D,
               UART1_STOPBITS_1, 
               UART1_PARITY_NO, 
               UART1_SYNCMODE_CLOCK_DISABLE, 
               UART1_MODE_TXRX_ENABLE);

    return TRUE;
}

void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
    if(xRxEnable) {
        // Receiver Interrupt Enable
        UART1->CR2 |= UART1_CR2_RIEN;
    } else {
        // Receiver Interrupt Disable
        UART1->CR2 &= ~UART1_CR2_RIEN;
    }

    if(xTxEnable) {
        // Transmission Complete Interrupt Enable
        UART1->CR2 |= UART1_CR2_TCIEN;
    } else {
        // Transmission Complete Interrupt Disable
        UART1->CR2 &= ~UART1_CR2_TCIEN;
    }
}

/* Put a byte in the UARTs transmit buffer. This function is called
 * by the protocol stack if pxMBFrameCBTransmitterEmpty() has been
 * called. */
BOOL xMBPortSerialPutByte(CHAR ucByte)
{
    while((UART1->SR & UART1_FLAG_TXE) == 0x00);
    
    UART1->DR = ucByte;

    return TRUE;
}

/* Return the byte in the UARTs receive buffer. This function is called
 * by the protocol stack after pxMBFrameCBByteReceived() has been called.
 */
BOOL xMBPortSerialGetByte(CHAR * pucByte)
{
    // Read data, clear RXNE bit
    *pucByte = UART1->DR;
    
    return TRUE;
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty() which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte() to send the character.
 */
void vUARTTxReadyISR(void)
{
    pxMBFrameCBTransmitterEmpty();
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived(). The
 * protocol stack will then call xMBPortSerialGetByte() to retrieve the
 * character.
 */
void vUARTRxISR(void)
{
    pxMBFrameCBByteReceived();
}
